Dart SDK gn 项目组织分析

Dart SDK 是一个庞大的项目,采用 GN 进行项目管理,关于 GN 的介绍,请参见《基于 gn 和 ninja 的构建系统》。

根目录 .gn

根目录下的 .gn 是入口:

  1. buildconfig = "//build/config/BUILDCONFIG.gn":这行指定了构建配置文件的位置。// 表示从源代码树的根目录开始的路径。

  2. secondary_source = "//build/secondary/":这行定义了一个次要的源代码根目录。这通常用于存放不能直接放在源代码树中的 GN 构建文件,例如第三方源代码树的构建文件。

  3. script_executable = "python3":这行覆盖了默认的脚本执行程序,将其设置为 python3。这意味着所有的 GN 脚本都将使用 Python 3 来执行。


build/config/BUILDCONFIG.gn

该文件定义了 Dart SDK 的全局定义,分为几个部分。

PLATFORM SELECTION

这段代码主要用于设置和验证构建平台的相关参数。这里的参数包括操作系统(os)和 CPU 架构(cpu)。这些参数被分为三种类型:host(编译机器),target(目标构建平台),和 current(当前正在定义的配置)。

以下是这段代码的主要部分:

  1. if (target_os == "") { target_os = host_os }:如果没有指定目标操作系统(target_os),则默认使用编译机器的操作系统(host_os)。

  2. assert(host_cpu != "")assert(target_cpu != ""):这两行代码确保编译机器的 CPU 架构(host_cpu)和目标 CPU 架构(target_cpu)都已经被指定。

  3. if (current_cpu == "") { current_cpu = target_cpu }if (current_os == "") { current_os = target_os }:如果当前的 CPU 架构(current_cpu)或者当前的操作系统(current_os)没有被指定,那么默认使用目标 CPU 架构(target_cpu)或者目标操作系统(target_os)。


BUILD FLAGS

首先定义了一个全局变量 is_component_build,并设置其值为 false。用于指示是否进行组件构建。在组件构建中,每个组件(例如库或模块)都会被单独编译成自己的动态库,而不是全部链接到一个单一的可执行文件中。这可以加快增量构建的速度,因为修改一个组件只需要重新编译该组件,而不需要重新编译整个项目。

然而,有一条注释说明 Dart VM 不支持组件构建,但是一些依赖项的构建文件会检查这个标志。这意味着,尽管 Dart VM 本身不会进行组件构建,但是它的一些依赖项可能需要这个标志来决定它们自己的构建方式。

然后,有一个 declare_args() 函数,这个函数用于声明一系列的构建参数。这些参数包括:

这些参数可以在命令行中指定,如果没有指定,则使用 declare_args() 中定义的默认值。


OS DEFINITIONS

这段代码的主要目的是设置一些布尔值,以便在编写基于操作系统的条件时使用。这些布尔值包括 is_androidis_chromeosis_iosis_winis_macis_posixis_linux 等。

代码首先检查 current_os 的值,这是一个变量,应该在此代码段之前定义,并设置为当前操作系统的名称。然后,根据 current_os 的值,代码设置一系列的布尔变量,以表示当前操作系统是否为特定的操作系统。

例如,如果 current_os 的值为 "win",那么 is_win 将被设置为 true,而所有其他的 is_ 变量将被设置为 false。这意味着当前操作系统是 Windows。

这种方法的优点是,后续的代码可以使用这些 is_ 变量来轻松检查当前操作系统,而无需每次都检查 current_os 的值。例如,如果你想要编写一些只在 Android 上运行的代码,你可以简单地检查 is_android 是否为 true,而不是检查 current_os 是否等于 "android"。


BUILD OPTIONS

它检查了一些变量(is_clangis_asanis_lsanis_msanis_tsanis_ubsan),这些变量通常用于指示是否启用特定的编译器或者编译选项。例如,is_clang 可能用于指示是否使用 Clang 编译器,is_asan 可能用于指示是否启用地址清理器(AddressSanitizer)等。

如果当前不是使用 Clang 编译器,但是启用了任何一种清理器(Sanitizer),那么它会强制使用 Clang 编译器。这是因为这些清理器都需要 Clang 编译器才能正常工作。

最后,它设置了 use_flutter_cxx 变量,如果启用了 MemorySanitizer(内存清理器,由 is_msan 控制)或者 ThreadSanitizer(线程清理器,由 is_tsan 控制),那么 use_flutter_cxx 将被设置为 true。这可能意味着在这些情况下,构建系统将使用特定的编译选项或者编译器来编译 Flutter 的 C++ 代码。


TARGET DEFAULTS

这个文件主要定义了一些默认的构建配置,这些配置将应用于所有的构建目标。这些配置包括编译器设置、优化级别、符号设置、链接器设置等。

这个文件首先定义了一个名为 _native_compiler_configs 的列表,其中包含了一些通用的编译器配置。

然后,根据不同的条件(例如操作系统类型、是否使用特定的 C++ 库等),向 _native_compiler_configs 列表中添加了更多的配置。

接下来,文件定义了针对不同类型的构建目标(例如可执行文件、静态库、共享库等)的默认配置。这些配置主要是从 _native_compiler_configs 列表中继承的,但也可能添加了一些特定于目标类型的额外配置。

这段代码是在配置构建系统,特别是针对不同的目标平台和编译器设置。_native_compiler_configs 是一个列表,用于存储所有的配置,这些配置将用于生成本地可执行文件和库。以下是对各个部分的概述:

配置项 说明
基本配置 这些是所有目标平台和编译器都需要的基本配置,包括编译器设置、C++版本、Clang堆栈对齐、ARM FPU和Thumb指令集、Chromium代码、默认包含目录、RTTI设置、运行时库等。
Flutter C++配置 如果使用了Flutter C++, 则会添加对应的配置。
Windows配置 如果目标平台是Windows,会添加一些特定的Windows配置,如SDK、Unicode、版本等。
POSIX配置 如果目标平台是POSIX兼容的(如Linux或MacOS),则会添加一些特定的POSIX配置。
Fuchsia配置 如果目标平台是Fuchsia,会添加一些特定的Fuchsia配置。
Linux/Mac/Android配置 如果目标平台是Linux、Mac或Android,会添加对应的SDK配置。
Clang配置 如果使用的编译器是Clang,会添加一些特定的Clang配置。
优化和调试检查 根据是否是调试或发布版本,会添加对应的优化和调试配置。
zlib配置 添加zlib需要的默认优化配置。
符号设置 添加符号设置配置。
Windows链接器设置 如果目标平台是Windows,会添加一些特定的Windows链接器配置。
可执行文件默认配置 根据目标平台,会添加对应的可执行文件配置。
静态库默认配置 设置静态库的默认配置。
共享库默认配置 根据目标平台,会添加对应的共享库配置。
源集默认配置 设置源集的默认配置。
组件默认配置 设置组件的默认配置。

最后,文件使用 set_defaults 函数为每种类型的构建目标设置了默认配置。这意味着,当你在其他地方定义一个新的构建目标时,这些默认配置将自动应用于新目标。

set_defaults 是一个GN构建系统的函数,它用于设置给定类型的所有目标的默认值。在这个文件中,set_defaults被用于设置以下类型的目标的默认配置:

  1. 可执行文件("executable"):这个目标类型用于生成可执行文件。默认配置包括 _native_compiler_configs 中的所有配置,以及特定于平台的链接器配置(例如Windows、Mac、Linux或Android)。

  2. 静态库("static_library"):这个目标类型用于生成静态库。默认配置只包括 _native_compiler_configs 中的所有配置。

  3. 共享库("shared_library"):这个目标类型用于生成共享库或者在组件模式下的组件。默认配置包括 _native_compiler_configs 中的所有配置,以及一个共享库配置和特定于平台的链接器配置(例如Windows或Mac)。

  4. 源集("source_set"):这个目标类型用于编译一组源文件,但不链接它们。默认配置只包括 _native_compiler_configs 中的所有配置。

  5. 组件("component"):这个目标类型用于在非组件模式下的组件。默认配置只包括 _native_compiler_configs 中的所有配置。

这些默认配置可以在具体的目标定义中被覆盖或者添加新的配置。


TOOLCHAIN SETUP

这段代码主要做了两件事:

  1. 工具链设置:这部分代码设置了默认的工具链和主机工具链。工具链是一组用于编译和链接代码的工具集合,包括编译器、链接器等。主机工具链是用于编译运行在本地系统的代码的工具链。这部分代码根据不同的操作系统(如Windows、Android、Linux、Mac、Fuchsia)和编译器(如Clang)选择不同的工具链。

  2. 设置默认依赖:这部分代码为executableloadable_moduleshared_library目标设置了默认的依赖。如果no_default_deps变量为真,那么不会添加任何标准依赖。否则,会根据是否使用use_flutter_cxx和是否为Fuchsia系统来添加不同的依赖。

这段代码的主要目的是根据不同的环境和需求来配置构建系统,以便在不同的系统和环境中正确地编译和链接代码。

在GN构建系统中,set_default_toolchain是一个内置函数,用于设置默认的工具链。工具链是一组用于编译和链接代码的工具集合,包括编译器、链接器等。

set_default_toolchain函数接受一个字符串参数,该参数指定了默认工具链的标签。在你提供的代码中,这个标签通常是一个以"//build/toolchain/"开头的路径,后面跟着特定于操作系统、编译器和CPU架构的部分。

例如,set_default_toolchain("//build/toolchain/win:clang_$current_cpu")会设置默认工具链为Windows系统下的Clang编译器,其中$current_cpu是一个变量,表示当前的CPU架构。

这个函数的作用是,当GN生成Ninja构建文件时,如果没有明确指定工具链,那么就会使用这个默认工具链来编译和链接代码。

在GN(Generate Ninja)构建系统中,模板是一种特殊的函数,可以用来定义一组重复使用的构建规则。模板可以接受参数,并在模板体内部使用这些参数。模板的主要目的是减少重复的代码,使构建配置更加清晰和易于管理。

在你提供的代码中,template(_target_type)定义了一个模板,模板的名称是_target_type变量的值。这个模板接受一个参数target_name,并在模板体内部定义了一个目标。目标的类型和名称都来自于模板的参数。

模板可以通过target函数来调用。例如,如果你定义了一个名为executable的模板,那么你可以通过executable("my_program")来调用这个模板,创建一个名为my_program的可执行目标。

在这个模板中,还使用了forward_variables_from函数来转发调用者的变量,以及defined函数来检查变量是否已定义。这些都是GN构建系统的内置函数,用于处理模板和目标的参数和变量。

定义了一个模板,用于为executableloadable_moduleshared_library这三种类型的目标设置默认的依赖。这段代码的主要步骤如下:

  1. 使用foreach循环遍历三种目标类型:executableloadable_moduleshared_library

  2. 对于每种目标类型,定义一个模板。模板的名称就是目标类型的名称,例如executable

  3. 在模板中,定义一个目标。目标的类型和名称都来自于模板的参数。

  4. 使用forward_variables_from函数将调用者的所有变量(除了no_default_deps)转发到目标。

  5. 如果deps变量未定义,那么初始化它为一个空列表。

  6. 如果no_default_deps变量未定义,或者它的值为假,那么添加默认的依赖。具体的依赖取决于use_flutter_cxxis_fuchsia两个变量的值。

这段代码的主要作用是,当你定义一个executableloadable_moduleshared_library目标时,如果你没有明确指定依赖,那么这个模板会自动为你添加一些默认的依赖。这样可以简化构建配置,避免重复指定相同的依赖。


COMPONENT SETUP

这段代码主要做了以下几件事:

  1. 断言 is_component_build 为假。如果 is_component_build 为真,那么会抛出错误并停止执行。这是因为代码注释指出,独立的 Dart VM 不应尝试进行组件构建。

  2. 设置 component_mode"source_set"。这意味着构建模式被设置为源集(source set)模式。

在 GN 构建系统中,component_mode 是一个变量,它用于指定构建的模式。在这段代码中,component_mode 被设置为 "source_set",这意味着构建模式被设置为源集(source set)模式。

源集(source set)是 GN 构建系统中的一个概念,它表示一组源文件和它们的编译设置。当你在 GN 构建文件中定义一个源集,你需要指定源文件和编译设置,然后 GN 会生成相应的构建规则。

在这段代码中,component 模板创建了一个源集,并使用 invoker 提供的参数配置了这个源集。这意味着,当你使用这个模板时,你可以提供一组源文件和编译设置,然后这个模板会生成相应的构建规则。

总的来说,component_mode = "source_set" 这行代码的意思是,设置构建模式为源集模式,即构建规则将基于源文件和编译设置生成。

这个模板中的每个 if 语句都在检查 invoker 是否定义了特定的配置。如果定义了,那么就将该配置的值设置为 invoker 提供的值。这些配置包括但不限于编译器标志(如 cflagscflags_c 等)、依赖项(如 deps)、库(如 libs)等。

总的来说,这段代码定义了一个 GN 构建模板,该模板用于创建源集(source set)目标,并根据提供的参数配置该目标。



本文作者:Maeiee

本文链接:Dart SDK gn 项目组织分析

版权声明:如无特别声明,本文即为原创文章,版权归 Maeiee 所有,未经允许不得转载!


喜欢我文章的朋友请随缘打赏,鼓励我创作更多更好的作品!